#include "FastSPI_LED2.h"
#include <EEPROM.h>
#include <SPI.h>
#include <RF24.h>
#include <nRF24L01.h>
#include "Reciever.h"
#define LEDLIB_GRB_ORDER  1       // GRB leds
#include "LEDFx.h"
#include "StarburstCommon.h"
#include "CustomWS2811Controller.h"

#define LED_PIN1 8
#define LED_PIN2 7
#define DATA_MESSAGE_POS 0
#define LAST_MESSAGE_POS 1
#define SHIFT_RATE .0001
struct RGB buffer[LED_COUNT];
CustomWS2811Controller<LED_PIN1, LED_PIN2> fastSpi;
LEDFxLib ledFx(LED_COUNT);
Reciever recv(9,10);

EffectSineWave wave(cRGB(0,0,0),cRGB(0,0,0),3);
EffectSolidColor solid;
EffectRainbow rainbow;
EffectCustom burstChaser(burstChaserFunc);
RGB burstPixelColor;
Mode currentMode;
ModeMessage disconnected(Disconnect, 0, 0, 200);

//-- Audio mode
uint8_t dimPulseValue;
uint8_t brightPulseValue;
uint8_t saturation;
LegData * audioData;
float colorShift = 0;
#define GRID_SIZE 37
uint8_t grid[GRID_SIZE][GRID_SIZE];



void setup()
{
	Serial.begin(115200);

	// setup the light strip. 
	fastSpi.init();
	recv.init();
	ledFx.init(buffer);
//	fillGrid();

	burstChaser.setFramerate(30);
	wave.setFramerate(1000);
	rainbow.setFramerate(60);

	for(int i = 0; i < LEG_COUNT; i++)
	  ledFx.addRange(i, i*BUCKET_SIZE, BUCKET_SIZE);
	changeMode(&disconnected);
}

void fillGrid() {
	delay(4000);
	for(int i = 0; i < GRID_SIZE; i++)
		memset(grid[i],0, GRID_SIZE);
	float t = (2*PI)/LEG_COUNT;
	float r = 19;
	for(int i = 0; i < LEG_COUNT; i++) {
		float x = r*cos(t * (float)i) + 18.0;
		float y = r*sin(t * (float)i) + 18.0;
		LEDFxUtilities::drawLine(18,18,x,y,grid);
	}
	Serial.println("drawing grid");
	for(int i = 0; i <GRID_SIZE; i++) {
		for(int j = 0; j < GRID_SIZE; j++) {
			int v = grid[i][j];
			if(v < 10)
				Serial.print("0");
			if(v < 100)
				Serial.print("0");
		
			Serial.print(v);
			Serial.print(" ");
		}
		Serial.println();
	}

	for(int i = 0; i <GRID_SIZE; i++) {
		for(int j = 0; j < GRID_SIZE; j++) {
			float v = (float)((int)(sqrt(sq(i-r) + sq(j-r))*10.0))/10.0;
			if(v < 10)
				Serial.print("0");
		
			Serial.print(v);
			Serial.print(" ");
		}
		Serial.println();
	}
}


bool useLedFx = true;
void loop()
{
	ModeMessage * mmRes;
	void* result;
	int type = recv.readMessage(&(result));
	switch(type) {
	case MODE_CHANGE_RECIEVED:
		mmRes =(ModeMessage*)result;
		changeMode(mmRes);
		// Alas, high performance stuff can't afford to use ledFx
		useLedFx = mmRes->mode != Sound;
		break;
	case MIRRORED_LEG_DATA_RECIEVED:
		audioData = (LegData*)result;
		// Alas, high performance stuff can't afford to use ledFx
		useLedFx = false;
		drawAudioData(audioData, buffer, colorShift, saturation, dimPulseValue, brightPulseValue);
		fastSpi.showRGB((byte*)buffer, LED_COUNT);
		colorShift += SHIFT_RATE;
		if(colorShift >= 1.0)
			colorShift -= 1.0;
#ifdef SHOW_REFRESH_RATE
		refreshCount ++;
		if(refreshCount == 100) {
			unsigned long cTime = millis();
			Serial.print((1000.0/((float)cTime - dataStartTime)) * 100.0);
			Serial.println("hz");
			dataStartTime = cTime;
			refreshCount =0;
		}
#endif
		break;
		// we got data when we didn't expect it -- switch to data mode
	case UNEXPECTED_DATA_RECIEVED: {
		loadSavedMode(DATA_MESSAGE_POS);
	}
	break;
	case DISCONNECTED:
		useLedFx = true;
		changeMode(&disconnected);
		break;
	case NOTHING_RECIEVED:
		break;
	}
	if(useLedFx) {
		if(ledFx.update()) {
			fastSpi.showRGB((byte*)buffer, LED_COUNT);
		}

	}
}
void loadSavedMode(int position) {
	ModeMessage temp;
	if(getMessage(position, &temp)) {
		Serial.print("Loading Mode: ");
		Serial.print(temp.mode);
		changeMode(&temp);
		recv.setCurrentMode(temp.mode);
	}
	else
		changeMode(&disconnected);
}

void changeMode(ModeMessage *msg) {
	switch(msg->mode) {
	case Loading:
		burstPixelColor = cRGB(msg->arg1,msg->arg2,msg->arg3);
		if(currentMode != msg->mode)
			ledFx.setCurrentEffect(burstChaser);
		break;
	case Wave:
		wave.setColor(rgbFromHSVArg(msg), 
			rgbFromHSVArg(0,0,0));
		if(currentMode != msg->mode)
			ledFx.setCurrentEffect(wave);
		break;
	case Rainbow:
		rainbow.setProperties(96, msg->arg2, msg->arg3, false);
		if(currentMode != msg->mode) {
			for(int i = 0; i < LEG_COUNT; i++)
				ledFx.setCurrentEffect(rainbow,i);
			ledFx.clearCurrentEffect();
		}
		break;
	case Solid:
		solid.setColor(rgbFromHSVArg(msg));
		if(currentMode != msg->mode)
			ledFx.setCurrentEffect(solid);
		break;
	case Disconnect:
		burstPixelColor = cRGB(msg->arg1,msg->arg2,msg->arg3);
		if(currentMode != msg->mode)
			ledFx.setCurrentEffect(burstChaser);
		break;
	case Sound:
		dimPulseValue = msg->arg1;
		brightPulseValue = msg->arg2;
		saturation = msg->arg3;
		if(currentMode != msg->mode) {
			saveMessage(DATA_MESSAGE_POS,msg);
			dataStartTime = millis();
			refreshCount =0;
		}
		break;
	}
	saveMessage(LAST_MESSAGE_POS,msg);
	currentMode = msg->mode;
}

RGB rgbFromHSVArg(ModeMessage * msg) {
	return rgbFromHSVArg(msg->arg1, msg->arg2, msg->arg3);
}
RGB rgbFromHSVArg(uint8_t arg1, uint8_t arg2, uint8_t arg3) {
	return LEDFxUtilities::HSVtoRGB(arg1,arg2,arg3);
}
//-- Loading mode
RGB burstChaserFunc(uint32_t time, uint16_t frame, uint16_t pixel) {
	if((pixel % BUCKET_SIZE) == (frame % LEG_COUNT)) {
		return burstPixelColor;
	}
	else {
		return EMPTY_COLOR;
	}
}

#define MSG_MAGIC_NUM 0xA4
void saveMessage(int position, ModeMessage * msg) {
	// messages are offset by 100 bytes.

	int pos = (position *5)+ 100;
	if(EEPROM.read(pos) != MSG_MAGIC_NUM) 
		EEPROM.write(pos, MSG_MAGIC_NUM);
	if(EEPROM.read(pos+1) != msg->mode) 
		EEPROM.write(pos+1, msg->mode);
	if(EEPROM.read(pos+2) != msg->arg1) 
		EEPROM.write(pos+2, msg->arg1);
	if(EEPROM.read(pos+3) != msg->arg2) 
		EEPROM.write(pos+3, msg->arg2);
	if(EEPROM.read(pos+4) != msg->arg3) 
		EEPROM.write(pos+4, msg->arg3);
}

bool getMessage(int position, ModeMessage * msg) {
	int pos = (position *5)+ 100;
	if(EEPROM.read(pos) != MSG_MAGIC_NUM)
		return false;

	msg->mode = (Mode)EEPROM.read(pos+1);
	msg->arg1 = EEPROM.read(pos+2);
	msg->arg2 = EEPROM.read(pos+3);
	msg->arg3 = EEPROM.read(pos+4);
	return true;
}

